1.15 Memory Management in C Programming

Module 1.15 • Architectural Segments, Static & Dynamic Allocation, Manual Heap Release

1.15.1 Introduction

Memory management is one of the most important concepts in C programming. Unlike many modern programming languages, C does not automatically manage memory. The programmer must allocate memory when needed and release it when it is no longer required.

Efficient memory management helps programs:

Understanding memory management is essential for developing reliable and efficient applications.

1.15.2 Memory Organization in C

When a C program executes, memory is divided into different sections.

+----------------------+
|      Code Segment    |
+----------------------+
|      Data Segment    |
+----------------------+
|         Heap         |
+----------------------+
|         Stack        |
+----------------------+

Each section serves a different purpose.

1.15.3 Stack Memory

Stack memory is automatically managed by the compiler.

Characteristics

Example

#include<stdio.h>
 
int main()
{
    int marks = 85;
 
    printf("%d", marks);
 
    return 0;
}

Here, marks is stored in stack memory.

Lifetime of Stack Variables

Memory is allocated when a function starts and automatically released when the function ends.

Example:

void demo()
{
    int num = 40;
}

When demo() finishes execution, memory occupied by num is released automatically.

1.15.4 Heap Memory

Heap memory is used for dynamic memory allocation.

Characteristics

Example applications:

1.15.5 Data Segment

Stores:

Example:

int total = 100;
static int count = 5;

These variables remain available throughout program execution.

1.15.6 Code Segment

Contains executable machine instructions of the program.

Example:

printf("Hello");

The compiled instructions are stored in the code segment.

1.15.7 Static Memory Allocation

Memory size is determined before program execution.

Example:

int numbers[10];

Memory is reserved during compilation.

Advantages

Limitations

Example Program

#include<stdio.h>
 
int main()
{
    int scores[4] = {55, 68, 72, 90};
 
    for(int i=0;i<4;i++)
    {
        printf("%d ", scores[i]);
    }
 
    return 0;
}

Output:

55 68 72 90

1.15.8 Dynamic Memory Allocation

Dynamic memory allocation allows memory to be allocated during runtime.

Benefits:

C provides four important functions:

malloc()
calloc()
realloc()
free()

All are available in:

#include <stdlib.h>

1.15.9 malloc()

malloc stands for Memory Allocation. It allocates a block of memory and returns a pointer to the first byte.

Syntax

ptr = (datatype*) malloc(size);

Example: Allocate Memory for 6 Integers

#include<stdio.h>
#include<stdlib.h>
 
int main()
{
    int *ptr;
 
    ptr = (int*)malloc(6 * sizeof(int));
 
    if(ptr == NULL)
    {
        printf("Allocation Failed");
        return 1;
    }
 
    for(int i=0;i<6;i++)
    {
        ptr[i] = (i + 1) * 15;
    }
 
    for(int i=0;i<6;i++)
    {
        printf("%d ", ptr[i]);
    }
 
    free(ptr);
 
    return 0;
}

Output:

15 30 45 60 75 90

Important Note

Memory allocated by malloc contains garbage values initially.

Example:

int *ptr;
 
ptr = (int*)malloc(4 * sizeof(int));

Contents are undefined until assigned.

1.15.10 calloc()

calloc stands for Contiguous Allocation. It allocates memory and initializes all bytes to zero.

Syntax

ptr = (datatype*)calloc(number_of_elements, size_of_each_element);

Example Program

#include<stdio.h>
#include<stdlib.h>
 
int main()
{
    int *ptr;
 
    ptr = (int*)calloc(5,sizeof(int));
 
    if(ptr == NULL)
    {
        printf("Allocation Failed");
        return 1;
    }
 
    for(int i=0;i<5;i++)
    {
        printf("%d ", ptr[i]);
    }
 
    free(ptr);
 
    return 0;
}

Output:

0 0 0 0 0

1.15.11 malloc vs calloc

Feature malloc() calloc()
InitializationGarbage ValuesZero Initialized
ArgumentsOneTwo
SpeedSlightly FasterSlightly Slower
UsageGeneral AllocationArrays

1.15.12 realloc()

realloc changes the size of previously allocated memory.

Syntax

ptr = realloc(ptr,new_size);

Used when data grows or shrinks dynamically.

Example Program

#include<stdio.h>
#include<stdlib.h>
 
int main()
{
    int *ptr;
 
    ptr = (int*)malloc(3 * sizeof(int));
 
    if(ptr == NULL)
    {
        return 1;
    }
 
    ptr[0] = 10;
    ptr[1] = 20;
    ptr[2] = 30;
 
    ptr = realloc(ptr,6 * sizeof(int));
 
    if(ptr == NULL)
    {
        return 1;
    }
 
    ptr[3] = 40;
    ptr[4] = 50;
    ptr[5] = 60;
 
    for(int i=0;i<6;i++)
    {
        printf("%d ",ptr[i]);
    }
 
    free(ptr);
 
    return 0;
}

Output:

10 20 30 40 50 60

Why realloc is Useful

Suppose a user enters unknown amounts of data. Instead of allocating huge memory initially:

int *data = malloc(1000*sizeof(int));

You can start small:

int *data = malloc(10*sizeof(int));

and expand later using: realloc(). This improves memory efficiency.

1.15.14 free()

free releases dynamically allocated memory.

Syntax

free(ptr);

After freeing memory: ptr = NULL; is recommended.

Example Program

#include<stdio.h>
#include<stdlib.h>
 
int main()
{
    int *ptr;
 
    ptr = (int*)malloc(4*sizeof(int));
 
    free(ptr);
 
    ptr = NULL;
 
    return 0;
}

Why Set Pointer to NULL?

After free: free(ptr);, the pointer still contains an old address. This creates a dangling pointer.

Safe practice:

free(ptr);
ptr = NULL;

1.15.16 Dynamic Array Example

#include<stdio.h>
#include<stdlib.h>
 
int main()
{
    int n;
 
    printf("Enter size: ");
    scanf("%d",&n);
 
    int *arr;
 
    arr = (int*)malloc(n*sizeof(int));
 
    if(arr == NULL)
    {
        printf("Allocation Failed");
        return 1;
    }
 
    for(int i=0;i<n;i++)
    {
        arr[i] = (i+1)*5;
    }
 
    printf("Array Elements:\n");
 
    for(int i=0;i<n;i++)
    {
        printf("%d ",arr[i]);
    }
 
    free(arr);
 
    return 0;
}

Sample Output:

Enter size: 5
 
Array Elements:
5 10 15 20 25

1.15.17 Dynamic Memory for Structures

Memory can also be allocated for structures.

struct Employee
{
    int id;
    float salary;
};

Allocation:

struct Employee *emp;
 
emp = (struct Employee*) malloc(sizeof(struct Employee));

Example Program

#include<stdio.h>
#include<stdlib.h>
 
struct Employee
{
    int id;
    float salary;
};
 
int main()
{
    struct Employee *emp;
 
    emp = malloc(sizeof(struct Employee));
 
    emp->id = 101;
    emp->salary = 45000.50;
 
    printf("%d\n",emp->id);
    printf("%.2f\n",emp->salary);
 
    free(emp);
 
    return 0;
}

Output:

101
45000.50

1.15.18 Common Memory Errors

Improper memory handling can create serious bugs.

Memory Leak

Memory is allocated but never released.

Bad Example:

int *ptr;
 
ptr = malloc(sizeof(int));
 
return 0;

Memory remains occupied.

Correct:

free(ptr);

Dangling Pointer

Pointer references memory already released.

Bad Example:

free(ptr);
 
printf("%d",*ptr);

Undefined behavior.

Double Free

Freeing same memory twice.

Bad Example:

free(ptr);
free(ptr);

May crash the program.

Buffer Overflow

Writing beyond allocated memory.

Example:

int *ptr;
 
ptr = malloc(3*sizeof(int));
 
ptr[5] = 100;

Undefined behavior.

1.15.19 Memory Leak Demonstration

Incorrect:

void test()
{
    int *ptr;
 
    ptr = malloc(100*sizeof(int));
}

Every function call leaks memory.

Correct:

void test()
{
    int *ptr;
 
    ptr = malloc(100*sizeof(int));
 
    free(ptr);
}

1.15.20 Best Practices

Always Check Allocation

if(ptr == NULL)
{
    printf("Allocation Failed");
}

Free Memory When Finished

free(ptr);

Set Pointer to NULL

free(ptr);
ptr = NULL;

Avoid Accessing Freed Memory

Incorrect:

free(ptr);
 
printf("%d",ptr[0]);

Allocate Exact Size

Good: malloc(n*sizeof(int));. Avoid arbitrary sizes.

1.15.21 Memory Allocation Flow

Request Memory
      ↓
malloc/calloc
      ↓
  Use Memory
      ↓
realloc (optional)
      ↓
     free
      ↓
Set Pointer NULL

1.15.22 Real-World Uses

Dynamic memory allocation is heavily used in:

Summary

Verify Comprehension: Technical Knowledge Assessment

Click your choice for each question to view feedback immediately. Complete all questions to evaluate your metric score.